<?php
/**
 * @author  HID丨emotion
 * @license http://www.hiders.cn
 * @version 2019/1/22 15:09
 */

namespace hiders\wechat;

use hiders\common\Cache;
use hiders\common\Config;
use hiders\http\Request;

class Wechat
{
    protected static $handler = null;
    protected static $config  = [];

    private $accessTokenUrl  = 'https://api.weixin.qq.com/cgi-bin/token';
    private $authorizeUrl    = 'https://open.weixin.qq.com/connect/oauth2/authorize';
    private $userInfoUrl     = 'https://api.weixin.qq.com/sns/userinfo';
    private $userInfoUrlBase = 'https://api.weixin.qq.com/cgi-bin/user/info';

    private $appId     = '';
    private $appSecret = '';

    private function __construct()
    {
        if (!isset($_SESSION)) {
            session_start();
        }
    }

    public static function Instance($appId = '', $appSecret = '', $host = '')
    {
        if (is_null(self::$handler)) {
            self::$handler = new self();
        }

        if (empty(self::$config)) {
            self::$config = Config::get('wechat');
        }

        self::$handler->appId     = $appId ?: self::$config['appid'];
        self::$handler->appSecret = $appSecret ?: self::$config['secret'];

        if (empty($host)) {
            $host = self::$config['host'];
        }

        if (!empty($host)) {
            self::$handler->accessTokenUrl = $host . '/cgi-bin/token';
            self::$handler->authorizeUrl   = $host . '/connect/oauth2/authorize';
            // self::$handler->userInfoUrl    = $host . '/sns/userinfo';
            //self::$handler->userInfoUrlBase = $host . '/cgi-bin/user/info';
        }

        return self::$handler;
    }

    public function getAccessToken($refresh = false)
    {
        $accessToken = Cache::get($this->appId . '_accessToken');
        if ($refresh || empty($accessToken)) {
            $url = $this->accessTokenUrl . '?grant_type=client_credential&appid=' . $this->appId . '&secret=' . $this->appSecret;
            $res = json_decode($this->httpGet($url));
            if (isset($res->access_token)) {
                $accessToken = $res->access_token;
                Cache::set($this->appId . '_accessToken', $accessToken, intval($res->expires_in / 2));
            } else {
                exit(empty($res) ? 'appId 错误！' : json_encode($res));
            }
        }

        return $accessToken;
    }

    public function getOauthAccessToken()
    {
        $this->httpGet($this->getAccessTokenUrl());
        $res = $this->httpGet($this->getAccessTokenUrl());
        if (!empty($res)) {
            $res = json_decode($res, true);
            if (isset($res['openid'])) {
                $_SESSION['openid'] = $res['openid'];

                return $res;
            } else {
                exit('获取 Access Token 出错：' . json_encode($res));
            }
        } else {
            exit('获取用户信息失败！');
        }
    }

    public function getOauthUserInfo($refresh = false, $oauthScope = 'snsapi_base')
    {
        if (is_null($oauthScope)) {
            $oauthScope = self::$config['oauth_scope'] ?: 'snsapi_base';
        }
        $cacheName = 'userInfo_' . $oauthScope;
        $userInfo  = isset($_SESSION[ $cacheName ]) ? $_SESSION[ $cacheName ] : [];
        if ($refresh || empty($userInfo)) {
            if (isset($_GET['code'])) {
                if (isset($userInfo['code']) && $userInfo['code'] == $_GET['code']) {
                    return $userInfo;
                }
                $accessToken              = $this->getOauthAccessToken();
                $url                      = $this->getUserInfoUrl($accessToken['access_token'], $accessToken['openid'], $oauthScope);
                $res                      = $this->httpGet($url);
                $userInfo                 = json_decode($res, true);
                $userInfo['code']         = $_GET['code'];
                $userInfo['access_token'] = $accessToken;
                $_SESSION[ $cacheName ]   = $userInfo;
            } else {
                header('Location:' . $this->getAuthorizeUrl($oauthScope));
                exit;
            }
        }

        return $userInfo;
    }

    private function getAuthorizeUrl($scope = 'snsapi_base')
    {
        //Oauth 标准参数
        $params = array(
            'appid'         => $this->appId,
            'redirect_uri'  => Request::getSelfUrl(),
            'response_type' => 'code',
            'scope'         => $scope,
            'state'         => '',
        );

        return $this->authorizeUrl . '?' . http_build_query($params) . '#wechat_redirect';
    }

    private function getAccessTokenUrl()
    {
        $params = array(
            'appid'      => $this->appId,
            'secret'     => $this->appSecret,
            'code'       => $_GET['code'],
            'grant_type' => 'authorization_code',
        );

        return $this->accessTokenUrl . '?' . http_build_query($params);
    }

    private function getUserInfoUrl($oauthToken, $openId, $scope = 'snsapi_base')
    {
        $params = [
            'access_token' => $oauthToken,
            'openid'       => $openId,
            'lang'         => 'zh_CN',
        ];

        switch ($scope) {
            case 'snsapi_userinfo':
                return $this->userInfoUrl . '?' . http_build_query($params);
                break;
            default:
                $params['access_token'] = $this->getAccessToken();

                return $this->userInfoUrlBase . '?' . http_build_query($params);
        }
    }

    private function httpGet($url)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        // 为保证第三方服务器与微信服务器之间数据传输的安全性，所有微信接口采用https方式调用，必须使用下面2行代码打开ssl安全校验。
        // 如果在部署过程中代码在此处验证失败，请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);

        $res = curl_exec($curl);
        curl_close($curl);

        return $res;
    }
}